package cn.com.bluemoon.daps.system.controller;

import cn.com.bluemoon.daps.api.notice.RemoteNoticeHandlerService;
import cn.com.bluemoon.daps.common.datascope.BmPermission;
import cn.com.bluemoon.daps.common.datascope.DataEncrypt;
import cn.com.bluemoon.daps.common.datascope.IgnorePermission;
import cn.com.bluemoon.daps.common.datascope.log.OperateLog;
import cn.com.bluemoon.daps.common.datascope.log.enums.ModuleObject;
import cn.com.bluemoon.daps.common.datascope.log.enums.OperateType;
import cn.com.bluemoon.daps.common.datascope.log.holder.ProcessLogHolder;
import cn.com.bluemoon.daps.common.domain.ResultBean;
import cn.com.bluemoon.daps.common.domain.UserInfoHolder;
import cn.com.bluemoon.daps.common.exception.DapException;
import cn.com.bluemoon.daps.common.toolkit.BmAssetUtils;
import cn.com.bluemoon.daps.domp.api.IDompService;
import cn.com.bluemoon.daps.domp.api.UserService;
import cn.com.bluemoon.daps.domp.api.param.UserDto;
import cn.com.bluemoon.daps.notice.core.NoticeType;
import cn.com.bluemoon.daps.notice.template.MetadataImpactMessage;
import cn.com.bluemoon.daps.notice.template.PreMetadataImpactTxtMessage;
import cn.com.bluemoon.daps.system.auth.JwtUtils;
import cn.com.bluemoon.daps.system.dto.NoticeSendRequestDto;
import cn.com.bluemoon.daps.system.entity.DapNoticeConfirmInfo;
import cn.com.bluemoon.daps.system.entity.DapNoticeInfo;
import cn.com.bluemoon.daps.system.entity.DapNoticeTemplateImpact;
import cn.com.bluemoon.daps.system.entity.DapSystemInfo;
import cn.com.bluemoon.daps.system.enums.NoticeChangeType;
import cn.com.bluemoon.daps.system.service.DapNoticeConfirmInfoService;
import cn.com.bluemoon.daps.system.service.DapNoticeInfoService;
import cn.com.bluemoon.daps.system.service.DapNoticeTemplateImpactService;
import cn.com.bluemoon.daps.system.service.DapSystemInfoService;
import cn.com.bluemoon.daps.system.vo.*;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 消息管理 前端控制器
 * </p>
 *
 * @author admin
 * @since 2021-09-23
 */
@Api(tags = "消息管理")
@RestController
@Slf4j
@RequestMapping("/notice")
@Validated
public class DapNoticeController {
    @Autowired
    private DapNoticeInfoService noticeInfoService;

    @Autowired
    private RemoteNoticeHandlerService remoteNoticeHandlerService;

    @Autowired
    private DapNoticeTemplateImpactService templateImpactService;

    @Autowired
    private DapNoticeConfirmInfoService noticeConfirmInfoService;

    @Autowired
    private IDompService dompService;

    @Autowired
    private DapSystemInfoService systemInfoService;

    @Autowired
    private UserService userService;

    @Value("${notice.confirm.urlFormat:https://dompapi.bluemoon.com.cn/dap/v1.0/system/notice/notice/confirm?token=%s}")
    private String confirmHrefFormat;

    @Value("${notice.confirm.expireDay:30}")
    private Long confirmExpireDay;

    @PostMapping
    @ApiOperation(value = "分页列表")
    @BmPermission(value = "system:notice:list", name = "分页列表查询", ignore = true)
    @ApiResponses(value = {@ApiResponse(code = 200, message = "ok", response = NoticeResponseVo.class, responseContainer = "List")})
    public ResultBean<IPage<NoticeResponseVo>> page(@Valid @RequestBody NoticePageRequestVo<DapNoticeInfo> requestVo) {
        LambdaQueryWrapper<DapNoticeInfo> query = Wrappers.<DapNoticeInfo>lambdaQuery()
                .and(StringUtils.isNotBlank(requestVo.getKeyword()), w -> w.like(DapNoticeInfo::getNoticeTitle, requestVo.getKeyword())
                        .or().like(DapNoticeInfo::getNoticeContent, requestVo.getKeyword()))
                .eq(requestVo.getNoticeTemplate() != null, DapNoticeInfo::getNoticeTemplate, requestVo.getNoticeTemplate())
                .orderByDesc(DapNoticeInfo::getSendTime);
        IPage<NoticeResponseVo> res = noticeInfoService.page(requestVo.getMpPage(), query).convert(this::toNoticeResponseVo);
        return ResultBean.ok(res);
    }

    @GetMapping
    @ApiOperation(value = "列表-标题-点击")
    @BmPermission(value = "system:notice:list", name = "列表-标题-点击", ignore = true)
    @ApiResponses(value = {@ApiResponse(code = 200, message = "ok", response = NoticeResponseDetailVo.class)})
    public ResultBean<NoticeResponseDetailVo> view(@ApiParam(value = "列表id", required = true) @RequestParam("id") Integer id) {
        UserInfoHolder.UserInfo userInfo = UserInfoHolder.getUserInfoOrEmpty();

        NoticeResponseDetailVo detailVo = new NoticeResponseDetailVo();
        DapNoticeInfo noticeInfo = noticeInfoService.getById(id);
        NoticeResponseDetailVo.BaseInfo baseInfo = new NoticeResponseDetailVo.BaseInfo();
        BeanUtils.copyProperties(noticeInfo, baseInfo);
        baseInfo.setReceiveUserIds(Arrays.stream(noticeInfo.getReceiveUserIds().split(",")).collect(Collectors.toList()));
        baseInfo.setCcUserIds(Arrays.stream(noticeInfo.getCcUserIds().split(",")).collect(Collectors.toList()));
        detailVo.setBaseInfo(baseInfo);

        DapNoticeTemplateImpact templateImpact = templateImpactService.getOne(Wrappers.<DapNoticeTemplateImpact>lambdaQuery().eq(DapNoticeTemplateImpact::getNoticeId, id).last(" limit 1"));
        NoticeResponseDetailVo.TemplateImpact templateInfo = new NoticeResponseDetailVo.TemplateImpact();
        BeanUtils.copyProperties(templateImpact, templateInfo);
        detailVo.setTemplate(templateInfo);

        List<DapNoticeConfirmInfo> confirmInfos = noticeConfirmInfoService.list(Wrappers.<DapNoticeConfirmInfo>lambdaQuery().eq(DapNoticeConfirmInfo::getNoticeId, id)
                .orderByDesc(DapNoticeConfirmInfo::getOperateTime).orderByAsc(DapNoticeConfirmInfo::getConfirmStatus));
        List<NoticeResponseDetailVo.ConfirmInfo> confirmInfoList = confirmInfos.stream().map(c -> {
            NoticeResponseDetailVo.ConfirmInfo confirmInfo = new NoticeResponseDetailVo.ConfirmInfo();
            BeanUtils.copyProperties(c, confirmInfo);
            try {
                ResultBean<List<UserDto>> res = dompService.matchUserInfosByUserId(userInfo.getToken(), c.getOperateUserId());
                List<UserDto> userDtos = res.getContent();
                if (!userDtos.isEmpty() && userDtos.get(0).getUser_name() != null) {
                    confirmInfo.setOperateUserName(userDtos.get(0).getUser_name());
                    confirmInfo.setOperateUserZw(userDtos.get(0).getZw());
                } else {
                    log.warn("该工号{}已失效或不存在用户信息", c.getOperateUserId());
                    confirmInfo.setConfirmStatus(3);
                }
            } catch (Exception e) {
                log.error("远程获取蓝数通用户详细信息失败", e);
            }
            return confirmInfo;
        }).collect(Collectors.toList());
        detailVo.setRelatedInfo(confirmInfoList);

        return ResultBean.ok(detailVo);
    }

    @GetMapping("/preview/resend")
    @ApiOperation(value = "预览重发")
    @BmPermission(value = "system:notice:list", name = "预览重发", ignore = true)
    @ApiResponses(value = {@ApiResponse(code = 200, message = "ok", response = NoticeResendResponseVo.class)})
    public ResultBean<NoticeResendResponseVo> preview(@ApiParam(value = "列表id", required = true) @RequestParam("id") Integer id) {
        NoticeResendResponseVo res = new NoticeResendResponseVo();
        DapNoticeInfo noticeInfo = noticeInfoService.getById(id);
        NoticeResendResponseVo.BaseInfo baseInfo = new NoticeResendResponseVo.BaseInfo();
        BeanUtils.copyProperties(noticeInfo, baseInfo);
        baseInfo.setReceiveUserIds(Arrays.stream(noticeInfo.getReceiveUserIds().split(",")).collect(Collectors.toList()));
        baseInfo.setCcUserIds(Arrays.stream(noticeInfo.getCcUserIds().split(",")).collect(Collectors.toList()));
        res.setBaseInfo(baseInfo);

        DapNoticeTemplateImpact templateImpact = templateImpactService.getOne(Wrappers.<DapNoticeTemplateImpact>lambdaQuery().eq(DapNoticeTemplateImpact::getNoticeId, id).last(" limit 1"));
        NoticeResendResponseVo.TemplateImpact templateInfo = new NoticeResendResponseVo.TemplateImpact();
        BeanUtils.copyProperties(templateImpact, templateInfo);
        res.setTemplate(templateInfo);
        return ResultBean.ok(res);
    }

    @PostMapping("/send")
    @ApiOperation(value = "发送")
    @BmPermission(value = "system:notice:list", name = "发送", ignore = true)
    @ApiResponses(value = {@ApiResponse(code = 200, message = "ok", response = Boolean.class)})
    @OperateLog(type = OperateType.UPDATE, moduleName = ModuleObject.SYSTEM)
    public ResultBean<Boolean> send(@Valid @RequestBody NoticeSendRequestDto sendRequestDto) {
        UserInfoHolder.UserInfo userInfo = UserInfoHolder.getUserInfoOrEmpty();
        // 发送
        ProcessLogHolder.append("用户" + userInfo.getAccount() + "发送消息");
        Boolean res = doSend(sendRequestDto, userInfo);
        return ResultBean.ok(res);
    }

    @PostMapping("/resend/{sourceNoticeId}")
    @ApiOperation(value = "重发")
    @BmPermission(value = "system:notice:list", name = "重发", ignore = true)
    @ApiResponses(value = {@ApiResponse(code = 200, message = "ok", response = Boolean.class)})
    @OperateLog(type = OperateType.INSERT, moduleName = ModuleObject.SYSTEM)
    public ResultBean<Boolean> resend(@ApiParam(value = "列表id/基于哪个消息id进行重发", required = true)
                                      @PathVariable("sourceNoticeId") Integer sourceNoticeId,
                                      @Valid @RequestBody NoticeSendRequestDto sendRequestDto) {
        UserInfoHolder.UserInfo userInfo = UserInfoHolder.getUserInfoOrEmpty();
        DapNoticeInfo noticeInfo = noticeInfoService.getById(sourceNoticeId);
        BmAssetUtils.isTrue(noticeInfo != null, "源消息通知已删除，请刷新！");
        ProcessLogHolder.append("用户" + userInfo.getAccount() + "重发消息" + sourceNoticeId);
        Boolean res = doSend(sendRequestDto, userInfo);
        return ResultBean.ok(res);
    }

    @GetMapping("/notice/confirm")
    @ApiOperation(value = "消息确认")
    @IgnorePermission(name = "消息确认")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "ok", response = Void.class)})
    @OperateLog(type = OperateType.OTHERS, moduleName = ModuleObject.SYSTEM)
    @DataEncrypt
    public String confirm(@RequestParam(value = "token") String token) {
        if (!JwtUtils.verifyToken(token, new Date())) {
            return ("该链接已失效！（认证无效或过期）");
        }
        Map<String, Object> jwtMap = JwtUtils.decodedToken(token);
        Long ackNoticeId = Long.valueOf(Objects.toString(jwtMap.getOrDefault(JwtUtils.businessId, null), null));
        String ackUserId = Objects.toString(jwtMap.getOrDefault(JwtUtils.certificateKey, null), null);
        Integer ackConfirmStatus = Integer.valueOf(Objects.toString(jwtMap.getOrDefault(JwtUtils.confirmStatus, null), "0"));
        NoticeConfirmVo confirmVo = new NoticeConfirmVo(ackNoticeId, ackUserId, null);
        ProcessLogHolder.append("收到...消息确认请求，确认码：" + confirmVo);
        String confirmUserId = getConfirmUserId(confirmVo);
        if (confirmUserId != null) {
            // 已确认过的，返回无需重复确认
            LambdaQueryWrapper<DapNoticeConfirmInfo> findOne = Wrappers.<DapNoticeConfirmInfo>lambdaQuery()
                    .eq(DapNoticeConfirmInfo::getNoticeId, confirmVo.getNoticeId())
                    .eq(DapNoticeConfirmInfo::getOperateUserId, confirmUserId)
                    .last("limit 1");
            Optional<DapNoticeConfirmInfo> confirmOpt = Optional.ofNullable(noticeConfirmInfoService.getOne(findOne));
            if (confirmOpt.isPresent()) {
                DapNoticeConfirmInfo confirmInfo = confirmOpt.get();
                if (ackConfirmStatus.equals(confirmInfo.getConfirmStatus())) {
                    return "无需多次确认";
                }
                LambdaUpdateWrapper<DapNoticeConfirmInfo> update = Wrappers.<DapNoticeConfirmInfo>lambdaUpdate()
                        .eq(DapNoticeConfirmInfo::getNoticeId, confirmVo.getNoticeId())
                        .eq(DapNoticeConfirmInfo::getOperateUserId, confirmUserId)
                        .set(DapNoticeConfirmInfo::getConfirmStatus, ackConfirmStatus)
                        .set(DapNoticeConfirmInfo::getOperateTime, LocalDateTime.now());
                noticeConfirmInfoService.update(update);
                ProcessLogHolder.append("收到...消息确认请求，确认完成");
                return ("提交成功");
            }
        }
        return ("无效请求");
    }

    /**
     * 发送
     */
    public Boolean doSend(NoticeSendRequestDto sendRequestDto, UserInfoHolder.UserInfo userInfo) {
        validateNoticeUser(sendRequestDto.getReceiveUserIds(), sendRequestDto.getCcUserIds());

        sendRequestDto.setReceiveUserEmails(new ArrayList<>(formatUserEmailByUserId(sendRequestDto.getReceiveUserIds()).values()));
        sendRequestDto.setCcUserEmails(new ArrayList<>(formatUserEmailByUserId(sendRequestDto.getCcUserIds()).values()));

        LocalDateTime sendTime = LocalDateTime.now();
        final String msgContent = getPreNoticeContent(sendRequestDto, userInfo);
        final Long noticeId = preSaveNotice(sendRequestDto, msgContent, sendTime, userInfo);
        MetadataImpactMessage metadataImpactMessage = buildNoticeMessage(noticeId, sendRequestDto, userInfo);

        Map<String, String> userIdAndEmails = new HashMap<>(formatUserEmailByUserId(sendRequestDto.getReceiveUserIds()));
        userIdAndEmails.putAll(formatUserEmailByUserId(sendRequestDto.getCcUserIds()));
        List<MetadataImpactMessage> metadataImpactMessages = divideMetadataImpactMessage(userIdAndEmails, noticeId, metadataImpactMessage);
        boolean allSendOk = doSendMessages(noticeId, metadataImpactMessages, userIdAndEmails);
        updateFinishedNotice(noticeId, allSendOk);
        return allSendOk;
    }

    private void validateNoticeUser(List<String> receiveUserIds, List<String> ccUserIds) {
        if (CollUtil.containsAny(receiveUserIds, ccUserIds)) {
            throw new DapException("收件人和抄送人不能同时存在");
        }
    }

    private void updateFinishedNotice(Long noticeId, boolean allSendOk) {
        LambdaUpdateWrapper<DapNoticeInfo> update = Wrappers.<DapNoticeInfo>lambdaUpdate().eq(DapNoticeInfo::getId, noticeId)
                .set(DapNoticeInfo::getNoticeStatus, Boolean.TRUE.equals(allSendOk) ? 1 : 0);
        noticeInfoService.update(update);
    }

    private boolean doSendMessages(Long noticeId, List<MetadataImpactMessage> metadataImpactMessages, Map<String, String> userIdAndEmails) {
        // 单独发送，都为true就认为发送成功，否则，失败
        Map<String, String> emailAndUserId = MapUtil.reverse(userIdAndEmails);
        boolean allSendOk = metadataImpactMessages.stream().allMatch(mim -> {
            if (mim.getRcs().stream().filter(StringUtils::isNotBlank).count() == 0) {
                log.warn("邮件为空，无效发送。");
                return true;
            }
            Optional<String> userValid = mim.getRcs().stream().findFirst().map(emailAndUserId::get);
            if (!userValid.isPresent()) {
                log.warn("该用户为无效，认为发送成功！");
                return true;
            }
            boolean sendStatus = false;
            try {
                ResultBean<Boolean> res = remoteNoticeHandlerService.sendMim(NoticeType.EMAIL, mim);
                sendStatus = Optional.ofNullable(res.getContent()).orElse(false);
            } catch (Exception e) {
                sendStatus = false;
            }
            String userId = userValid.get();
            LambdaUpdateWrapper<DapNoticeConfirmInfo> updateNoticeStatus = Wrappers.<DapNoticeConfirmInfo>lambdaUpdate().eq(DapNoticeConfirmInfo::getNoticeId, noticeId)
                    .eq(DapNoticeConfirmInfo::getOperateUserId, userId)
                    .set(DapNoticeConfirmInfo::getSendStatus, sendStatus ? 1 : 0);
            log.info("发送给{}邮件情况：{}", userId, sendStatus);
            noticeConfirmInfoService.update(updateNoticeStatus);
            return sendStatus;
        });
        return allSendOk;
    }

    private String getPreNoticeContent(NoticeSendRequestDto sendRequestDto, UserInfoHolder.UserInfo userInfo) {
        String msgContent = "";
        try {
            PreMetadataImpactTxtMessage preMetadataImpactTxtMessage = buildPreMetadataImpactTxtMessage(sendRequestDto, userInfo);
            ResultBean<String> preSendMsgInfoResultBean = remoteNoticeHandlerService.preSendInfo(NoticeType.EMAIL, preMetadataImpactTxtMessage);
            msgContent = preSendMsgInfoResultBean.getContent();
        } catch (Exception e) {
            throw new DapException("远程调用消息服务失败，暂不可发送邮件！", e);
        }
        return msgContent;
    }

    private List<MetadataImpactMessage> divideMetadataImpactMessage(Map<String, String> userIdAndEmails, Long noticeId, MetadataImpactMessage metadataImpactMessage) {
        Map<String, String> emailAndUserId = MapUtil.reverse(userIdAndEmails);
        Collection<String> rcsEmails = metadataImpactMessage.getRcs();
        List<MetadataImpactMessage> res = rcsEmails.stream().map(userEmail -> {
            MetadataImpactMessage m = new MetadataImpactMessage();
            BeanUtils.copyProperties(metadataImpactMessage, m);
            m.setRcs(Collections.singleton(userEmail));
            m.setCcs(Collections.emptyList());
            m.setFooter(getFormatMsgFooter(noticeId, emailAndUserId.get(userEmail)));
            return m;
        }).collect(Collectors.toList());
        List<String> ccsEmails = metadataImpactMessage.getCcs();
        List<MetadataImpactMessage> ccsMsg = ccsEmails.stream().map(userEmail -> {
            MetadataImpactMessage m = new MetadataImpactMessage();
            BeanUtils.copyProperties(metadataImpactMessage, m);
            m.setRcs(Collections.singleton(userEmail));
            m.setCcs(Collections.emptyList());
            m.setFooter("");
            return m;
        }).collect(Collectors.toList());
        res.addAll(ccsMsg);
        return res;
    }

    private Long preSaveNotice(NoticeSendRequestDto sendRequestDto,
                               String msgContent, LocalDateTime sendTime,
                               UserInfoHolder.UserInfo userInfo) {
        // 记录
        DapNoticeInfo noticeInfo = new DapNoticeInfo();
        noticeInfo.setNoticeTitle(formatTitle(sendRequestDto.getChangeTableName(), sendTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))));
        noticeInfo.setNoticeContent(msgContent);
        noticeInfo.setNoticeTemplate(1);
        noticeInfo.setReceiveUserIds(String.join(",", sendRequestDto.getReceiveUserIds()));
        List<String> ccUserIds = sendRequestDto.getCcUserIds();
        noticeInfo.setCcUserIds(String.join(",", ccUserIds));
        noticeInfo.setNoticeStatus(-1);
        noticeInfo.setNoticeMode(1);
        noticeInfo.setSendTime(sendTime);
        noticeInfo.setCreateUserId(userInfo.getAccount());
        noticeInfo.setCreateTime(LocalDateTime.now());
        noticeInfo.setLastUpdateUserId(userInfo.getAccount());
        noticeInfo.setLastUpdateTime(sendTime);
        noticeInfoService.save(noticeInfo);
        final Long noticeInfoId = noticeInfo.getId();
        log.info("记录邮件发送信息");

        DapNoticeTemplateImpact templateImpact = new DapNoticeTemplateImpact();
        BeanUtils.copyProperties(sendRequestDto, templateImpact);
        templateImpact.setChangeUser(sendRequestDto.getChangeUser());
        templateImpact.setChangeType(sendRequestDto.getChangeType());
        templateImpact.setSystemId(sendRequestDto.getSystemId());
        // 附加
        templateImpact.setCreateUserId(userInfo.getAccount());
        templateImpact.setCreateTime(LocalDateTime.now());
        templateImpact.setLastUpdateTime(sendTime);
        templateImpact.setNoticeId(noticeInfoId);
        templateImpact.setId(null);
        templateImpactService.save(templateImpact);
        log.info("记录邮件发送模板信息");

        Set<String> allUser = new HashSet<>(sendRequestDto.getReceiveUserIds());
        allUser.addAll(ccUserIds);
        // 2022/6/24 抄送人状态无需确认。
        Set<DapNoticeConfirmInfo> confirms = allUser.stream().map(u -> {
            DapNoticeConfirmInfo confirmInfo = new DapNoticeConfirmInfo();
            confirmInfo.setId(null);
            confirmInfo.setNoticeId(noticeInfoId);
            confirmInfo.setOperateUserId(u);
            confirmInfo.setConfirmStatus(ccUserIds.contains(u) ? 2 : 0);
            confirmInfo.setSendStatus(0);
            confirmInfo.setOperateTime(null);
            return confirmInfo;
        }).collect(Collectors.toSet());
        if (!confirms.isEmpty()) {
            noticeConfirmInfoService.saveBatch(confirms);
            log.info("记录邮件相关人");
        }
        return noticeInfoId;
    }

    private PreMetadataImpactTxtMessage buildPreMetadataImpactTxtMessage(NoticeSendRequestDto sendDto,
                                                                         UserInfoHolder.UserInfo userInfo) {
        LocalDateTime sendTime = LocalDateTime.now();
        String sendTimeStr = sendTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
        MetadataImpactMessage metadataImpactMsg = new MetadataImpactMessage();
        metadataImpactMsg.setHeader("表影响通知");
        metadataImpactMsg.setTopic(formatTitle(sendDto.getChangeTableName(), sendTimeStr));
        String changeUserName = getChangeUserInfo(sendDto.getChangeUser());
        metadataImpactMsg.setChangeUser(changeUserName);
        DapSystemInfo sysInfo = systemInfoService.getById(sendDto.getSystemId());
        BmAssetUtils.isTrue(sysInfo != null && sysInfo.getName() != null, "获取系统名失败");
        metadataImpactMsg.setSystemName(sysInfo.getName());
        metadataImpactMsg.setChangeTableSchema(sendDto.getChangeTableSchema());
        metadataImpactMsg.setChangeTableName(sendDto.getChangeTableName());
        metadataImpactMsg.setChangeScript(sendDto.getChangeScript());
        metadataImpactMsg.setChangeReason(sendDto.getChangeReason());
        metadataImpactMsg.setArchiveTableName(sendDto.getArchiveTableName());
        metadataImpactMsg.setArchiveInfo(sendDto.getArchiveInfo());
        metadataImpactMsg.setImpactAnalysisResult(sendDto.getImpactAnalysisResult());
        metadataImpactMsg.setChangeType(NoticeChangeType.valueOfCode(sendDto.getChangeType(), NoticeChangeType.TABLE_CHANGE).getName());
        metadataImpactMsg.setPlanChangeTime(sendDto.getPlanChangeTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")));
        metadataImpactMsg.setFooter("");
        metadataImpactMsg.setSender(userInfo.getUserName());
        metadataImpactMsg.setAction("");
        metadataImpactMsg.setRcs(sendDto.getReceiveUserEmails());
        metadataImpactMsg.setCcs(sendDto.getCcUserEmails());
        metadataImpactMsg.setBcc(Lists.newArrayList());
        metadataImpactMsg.setAttachmentMap(Maps.newHashMap());
        metadataImpactMsg.setSendTime(sendTime);
        return new PreMetadataImpactTxtMessage(metadataImpactMsg);
    }

    private MetadataImpactMessage buildNoticeMessage(Long noticeId, NoticeSendRequestDto sendDto,
                                                     UserInfoHolder.UserInfo userInfo) {
        LocalDateTime sendTime = LocalDateTime.now();
        String sendTimeStr = sendTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
        MetadataImpactMessage metadataImpactMsg = new MetadataImpactMessage();
        metadataImpactMsg.setHeader("表影响通知");
        metadataImpactMsg.setTopic(formatTitle(sendDto.getChangeTableName(), sendTimeStr));
        String changeUserName = getChangeUserInfo(sendDto.getChangeUser());
        metadataImpactMsg.setChangeUser(changeUserName);
        DapSystemInfo sysInfo = systemInfoService.getById(sendDto.getSystemId());
        BmAssetUtils.isTrue(sysInfo != null && sysInfo.getName() != null, "获取系统名失败");
        metadataImpactMsg.setSystemName(sysInfo.getName());
        metadataImpactMsg.setChangeTableSchema(sendDto.getChangeTableSchema());
        metadataImpactMsg.setChangeTableName(sendDto.getChangeTableName());
        metadataImpactMsg.setChangeScript(sendDto.getChangeScript());
        metadataImpactMsg.setChangeReason(sendDto.getChangeReason());
        metadataImpactMsg.setArchiveTableName(sendDto.getArchiveTableName());
        metadataImpactMsg.setArchiveInfo(sendDto.getArchiveInfo());
        metadataImpactMsg.setImpactAnalysisResult(sendDto.getImpactAnalysisResult());
        metadataImpactMsg.setChangeType(NoticeChangeType.valueOfCode(sendDto.getChangeType(), NoticeChangeType.TABLE_CHANGE).getName());
        metadataImpactMsg.setPlanChangeTime(sendDto.getPlanChangeTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")));
        ArrayList<String> userIds = new ArrayList<>(sendDto.getReceiveUserIds());
        userIds.addAll(sendDto.getCcUserIds());
        metadataImpactMsg.setFooter(getFormatMsgFooter(noticeId, String.join(";", userIds)));
        metadataImpactMsg.setSender(userInfo.getUserName());
        metadataImpactMsg.setAction("");
        metadataImpactMsg.setRcs(sendDto.getReceiveUserEmails());
        metadataImpactMsg.setCcs(sendDto.getCcUserEmails());
        metadataImpactMsg.setBcc(Lists.newArrayList());
        metadataImpactMsg.setAttachmentMap(Maps.newHashMap());
        metadataImpactMsg.setSendTime(sendTime);
        return metadataImpactMsg;
    }

    private Map<String, String> formatUserEmailByUserId(List<String> userIds) {
        Map<String, String> userEmail = userIds.stream().map(a -> userService.getUserInfoByUserId(a))
                .collect(Collectors.toMap(UserInfoHolder.UserInfo::getAccount, UserInfoHolder.UserInfo::getEmail, (a, b) -> b));
        return userEmail;
    }


    private String getFormatMsgFooter(Long noticeId, String userId) {
        return String.format("收到本邮件后请你认真核对变更的内容是否同意，如果同意请点击%s，如果不同意点击%s，请及时联系变更人反馈！！！",
                // 确认状态 0-未确认 1-已经确认同意 2-无需确认 3-失效账号 4-已确认不同意
                buildConfirmHrefTagWithToken(noticeId, userId, 1),
                buildConfirmHrefTagWithToken(noticeId, userId, 4));
    }

    private String buildConfirmHrefTagWithToken(Long noticeId, String userId, Integer confirmStatus) {
        String noticeToken = JwtUtils.createToken(ImmutableMap.of(JwtUtils.businessId, noticeId, JwtUtils.certificateKey, userId, JwtUtils.confirmStatus, confirmStatus), confirmExpireDay);
        String btnName;
        if (confirmStatus == 1) btnName = "同意";
        else if (confirmStatus == 4) btnName = "不同意";
        else btnName = "";
        return "<a href='" + String.format(confirmHrefFormat, noticeToken) + "'> " + btnName + " </a>";
    }

    private String getChangeUserInfo(String changeUserId) {
        String changeUserName = null;
        try {
            changeUserName = userService.getUserInfoByUserId(changeUserId).getUserName();
        } catch (Exception e) {
            return changeUserId;
        }
        return String.format("%s(%s)", changeUserName, changeUserId);
    }

    private String formatTitle(String tableName, String sendTimeStr) {
        return String.format("变更【%s】表影响通知（%s）", tableName, sendTimeStr);
    }

    private String getConfirmUserId(NoticeConfirmVo confirmVo) {
        if (confirmVo.getUserId() != null) {
            UserInfoHolder.UserInfo validUser = userService.getUserInfoByUserId(confirmVo.getUserId());
            BmAssetUtils.isTrue(validUser != null && validUser.getAccount() != null, "非法用户！");
            return confirmVo.getUserId();
        }
        if (confirmVo.getUserToken() != null) {
            String userId = userService.isLoginUser(confirmVo.getUserToken());
            BmAssetUtils.isTrue(userId != null, "认证获取用户信息为空！");
            return userId;
        }
        return confirmVo.getUserId();
    }


    private NoticeResponseVo toNoticeResponseVo(DapNoticeInfo noticeInfo) {
        NoticeResponseVo vo = new NoticeResponseVo();
        BeanUtils.copyProperties(noticeInfo, vo);
        vo.setSender(noticeInfo.getCreateUserId());
        if (vo.getId() == null) {
            vo.setId(noticeInfo.getId());
        }
        return vo;
    }

}
